package com.jwater.launch;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLClassLoader;
import java.util.Set;

import sun.misc.URLClassPath;

/**
 * @description:加载jar包
 * @author: jnner
 * @createDate: 2021/2/5
 * @version: 1.0
 */
public final class LoadJar {
    /**
     * 加载jar，需要指定classLoader
     *
     * @param classLoader
     * @param jarFile
     * @throws IOException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static void loadJarFile(ClassLoader classLoader, File jarFile)
            throws IOException, IllegalArgumentException, IllegalAccessException {
        if (jarFile == null || !jarFile.exists()) {
            return;
        }
        if (classLoader instanceof URLClassLoader) {
            Field[] fields = findSpecificClass(classLoader.getClass(), "URLClassLoader").getDeclaredFields();
            for (Field field : fields) {
                if ("ucp".equals(field.getName())) {
                    field.setAccessible(true);
                    URLClassPath ucp = (URLClassPath) field.get(classLoader);
                    ucp.addURL(jarFile.toURI().toURL());
                    return;
                }
            }
        } else {
            throw new IllegalAccessException("ClassLoader not extends URLClassLoader");
        }
    }

    /**
     * 加载jar
     *
     * @param jarFile
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws IOException
     */
    public static void loadJarFile(File jarFile) throws IllegalArgumentException, IllegalAccessException, IOException {
        ClassLoader loader = findExtClassLoader();
        loadJarFile(loader, jarFile);
    }

    /**
     * 加载目录下所有的jar包，需要指定classLoader
     *
     * @param classLoader
     * @param dirFile
     * @param recursive
     * @param excludes
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws IOException
     */
    public static void loadJarDir(ClassLoader classLoader, File dirFile, boolean recursive, Set<String> excludes)
            throws IllegalArgumentException, IllegalAccessException, IOException {
        if (dirFile == null || !dirFile.exists()) {
            return;
        }
        File[] jarFiles = dirFile.listFiles();
        for (File jarFile : jarFiles) {
            if (jarFile.isDirectory()) {
                if (recursive) {
                    loadJarDir(classLoader, jarFile, recursive, excludes);
                }
            } else if (jarFile.getName().endsWith(".jar")) {
                if (excludes == null) {
                    loadJarFile(classLoader, jarFile);
                } else {
                    if (!contain(excludes, jarFile.getName())) {
                        loadJarFile(classLoader, jarFile);
                    }
                }
            }
        }
    }

    /**
     * 加载目录下所有的jar包，可以指定排除哪些jar
     *
     * @param dirFile
     * @param recursive
     * @param excludes
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws IOException
     */
    public static void loadJarDir(File dirFile, boolean recursive, Set<String> excludes)
            throws IllegalArgumentException, IllegalAccessException, IOException {
        ClassLoader loader = findExtClassLoader();
        loadJarDir(loader, dirFile, recursive, excludes);
    }

    /**
     * 加载目录下所有的jar包
     *
     * @param dirFile
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws IOException
     */
    public static void loadJarDir(File dirFile) throws IllegalArgumentException, IllegalAccessException, IOException {
        loadJarDir(dirFile, false, null);
    }

    /**
     * 查找ExtClassLoader
     *
     * @return
     */
    protected static ClassLoader findExtClassLoader() {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        while (loader != null) {
            loader = loader.getParent();
            if (loader.getClass().getSimpleName().indexOf("ExtClassLoader") > -1) {
                return loader;
            }
        }
        return Thread.currentThread().getContextClassLoader();
    }

    @SuppressWarnings("rawtypes")
    protected static Class findSpecificClass(Class clazz, String name) {
        if (clazz.getName().indexOf(name) >= 0) {
            return clazz;
        } else {
            return findSpecificClass(clazz.getSuperclass(), name);
        }
    }

    protected static boolean contain(Set<String> excludes, String name) {
        return excludes.contains(name);
    }
}
